`define GLOBAL_BUSADDRWID 32
`define GLOBAL_BUSDATAWID 64
`define GPU_CACHE_DATAWID 64

`define GPU_DADDR_WIDTH 32
`define GPU_IADDR_WIDTH 32

`define GPU_LANE_WIDTH 16
`define GPU_LANE_NUM 8
`define GPU_VLEN `GPU_LANE_WIDTH*`GPU_LANE_NUM

`define GPU_XLEN32  //This is RV32IMF(V) ISA
`define USE_FMCX    //stick MUL/DIV together in single issue prcessor to save DSP block
`define PIPELINED_FPU   //enable pipelineing of extensions 
//FMCX automatically contains this two, or you must manually define these two extension
`ifdef USE_FMCX
`define USE_EXTM
`define USE_EXTF
`endif

`ifdef GPU_XLEN64
`define GPU_XLEN 64

`elsif GPU_XLEN32
`define GPU_XLEN 32

`else 
`define GPU_XLEN 32

`endif


module left_bshifter
#(
    parameter IN_WIDTH = 64,
    SHIFT_STAGE= ((IN_WIDTH > 1) ? $clog2(IN_WIDTH) : 1),
    SHIFT_WIDTH=2**(SHIFT_STAGE)
)
(
    input [IN_WIDTH-1:0]datain,
    input [SHIFT_STAGE-1:0]shiftnum,
    output [IN_WIDTH-1:0]dataout
);
reg [SHIFT_WIDTH-1:0]shift_stages[SHIFT_STAGE:0];
wire [SHIFT_WIDTH-1:0]shift_o;
wire sign;
integer i,j;
always @(*)
begin
    shift_stages[0]=datain;
    for(i=1;i<=SHIFT_STAGE;i=i+1)
    begin
        if(shiftnum[i-1])shift_stages[i]=shift_stages[i-1]<<(2**(i-1));
        else shift_stages[i]=shift_stages[i-1];
    end
end
assign shift_o=shift_stages[SHIFT_STAGE];
assign dataout=shift_o[IN_WIDTH-1:0];
endmodule 
module normalize_shifter
#(
    parameter result_width=11,
    normalize_width=10,
    SEL_WIDTH = ((result_width > 1) ? $clog2(result_width) : 1)
)
(
    input [result_width-1:0]datai,
    output [SEL_WIDTH-1:0]left_shift_num,
    output [normalize_width-1:0]datao
);
reg [7:0]shiftcnt_encode[result_width:0];
wire [result_width-1:0]aligned;
integer i;
always @(*)
begin
    shiftcnt_encode[0]=0;
    for ( i=1;i<=result_width;i=i+1) 
    begin
        shiftcnt_encode[i]=sel_entry_cell(shiftcnt_encode[i-1],i,datai[i-1]);
    end
end
assign left_shift_num=result_width-shiftcnt_encode[result_width];
defparam LEFT_ALIGNER.IN_WIDTH = result_width;
left_bshifter LEFT_ALIGNER
(
    .datain(datai),
    .shiftnum(left_shift_num),
    .dataout(aligned)
);
assign datao=aligned
            [result_width-2:(result_width-normalize_width-1)];
function [SEL_WIDTH-1:0]sel_entry_cell;
    input [SEL_WIDTH-1:0]prev_entry_num;
    input [SEL_WIDTH-1:0]cur_entry_num;
    input cur_entry_bit;
    begin
   		sel_entry_cell=(cur_entry_bit)?cur_entry_num:prev_entry_num;
   	end
endfunction
endmodule
module bshifter16
(
    input [15:0]datain,
    input [1:0]typ,
    input [3:0]shiftnum,
    output [15:0]dataout
);
wire [15:0]shift0;
wire [15:0]shift1;
wire [15:0]shift2;
wire [15:0]shift3;
assign shift0=(
    (shiftnum[0])?
    (typ[1]?{{1{datain[15]&typ[0]}},datain[15:1]}:{datain[14:0],1'b0}):
    (datain));
assign shift1=(
    (shiftnum[1])?
    (typ[1]?{{2{datain[15]&typ[0]}},shift0[15:2]}:{shift0[13:0],2'b0}):
    (shift0)
    );
assign shift2=(
    (shiftnum[2])?
    (typ[1]?{{4{datain[15]&typ[0]}},shift1[15:4]}:{shift1[11:0],4'b0}):
    (shift1)
    );
assign shift3=(
    (shiftnum[3])?
    (typ[1]?{{8{datain[15]&typ[0]}},shift2[15:8]}:{shift2[7:0],8'b0}):
    (shift2)
    );
assign dataout=shift3;
endmodule 
module FPU16
(
    input enable,
    input [15:0]op1,
    input [15:0]op2,
    input vec_en,
    input addsel,
    input subsel,
    input mulsel, 
    input itfsel, 
    input ftisel, 
    input maxsel,
    input minsel,
    input [31:0]fullin,
    input ftlsel,
    output divvalid, 
    output [31:0]fullout,
    output [15:0]opout,
    output gt,
    output eq
);
wire sign1,sign2;
wire [4:0]exp1;
wire [4:0]exp2;
wire exp_sgn1,exp_sgn2;
wire [9:0]val1;
wire [9:0]val2;
wire inf1,inf2,nan1,nan2;
wire zero1,zero2;
assign sign1=(ftlsel)?ftlout[15]:op1[15];
assign sign2=(vec_en)?ftlout[15]:op2[15];
assign exp1=(ftlsel)?ftlout[14:10]:op1[14:10];
assign exp2=(vec_en)?ftlout[14:10]:op2[14:10];
assign val1=(ftlsel)?ftlout[9:0]:op1[9:0];
assign val2=(vec_en)?ftlout[9:0]:op2[9:0];
assign nan1=(val1!=0&(exp1==5'h1f));
assign nan2=(val2!=0&(exp2==5'h1f));
assign inf1=(val1==0&(exp1==5'h1f));
assign inf2=(val2==0&(exp2==5'h1f));
assign zero1=(exp1==0&val1==0);
assign zero2=(exp2==0&val2==0);
wire [1:0]exp_cmp; 
wire [5:0]exp_diff;
wire [5:0]exp_dif1,exp_dif2;
wire [10:0]base_smaller;
wire [10:0]base_larger;
wire much_larger,diff_sym;
assign diff_sym=sign1^sign2;
assign exp_diff=exp_dif1-exp_dif2;
assign much_larger=(exp_diff>5'd10);
assign exp_cmp=(exp1==exp2)?((val1>val2)?2'b11:2'b10):
        (exp1>exp2)?2'b01:2'b00;
assign exp_dif1=(exp_cmp[0])?exp1:exp2;
assign exp_dif2=(!exp_cmp[0])?exp1:exp2;
assign base_larger=(exp_cmp[0])?{1'b1,val1}:{1'b1,val2};
assign base_smaller=(!exp_cmp[0])?{1'b1,val1}:{1'b1,val2};
assign eq=(op1==op2);
assign gt=(diff_sym)?((sign1==0)?1'b1:1'b0): 
        ((sign1==0)? !exp_cmp[0] : exp_cmp[0]);  
wire [15:0]acalc;
wire [11:0]fadd_calc;
wire [9:0]aout;
wire [10:0]aligned_out;
wire [3:0]fadd_align_exp;
wire [4:0]fadd_expout;
wire fadd_signcond;
wire fadd_signcalc;
bshifter16 FADD_EXP_ALIGNER
(
    .datain({5'h00,base_smaller}),
    .shiftnum(exp_diff[3:0]),
    .typ(2'b10),
    .dataout(aligned_out)
);
assign fadd_expout=exp_dif1-(fadd_align_exp-1'b1);
assign acalc=(much_larger)?
            (exp_cmp[0]?op1:op2):
            ({fadd_signcalc,fadd_expout,aout});
assign fadd_signcond=!(sign1^sign2^subsel);
assign fadd_calc=(fadd_signcond)?base_larger+aligned_out:    
                    base_larger+(~aligned_out)+1;
assign fadd_signcalc=(fadd_signcond)?sign1:((sign1)?exp_cmp[0]:!exp_cmp[0]);                    
defparam FADD_ALIGNER.result_width=12;
defparam FADD_ALIGNER.normalize_width=10;
normalize_shifter FADD_ALIGNER
(
    .datai(fadd_calc),
    .left_shift_num(fadd_align_exp),
    .datao(aout)
);
wire [21:0]valmul;
wire [5:0]expmul;
wire [15:0]mcalc;
wire [9:0]mulval_o;
wire [3:0]mexp_align;
wire fmul_overflow;
defparam FMUL_ALIGNER.result_width=22;
defparam FMUL_ALIGNER.normalize_width=10;
normalize_shifter FMUL_ALIGNER
(
    .datai(valmul),
    .left_shift_num(mexp_align),
    .datao(mulval_o)
);
assign valmul={1'b1,val1}*{1'b1,val2};
assign expmul=exp1+exp2-mexp_align-6'd14;
assign mcalc=(expmul<6'h1f)?{sign1^sign2,expmul[4:0],mulval_o}:{sign1^sign2,15'he800};
wire [15:0]itfcalc;
wire [3:0]exp_get;
wire [4:0]itf_exp;
wire [15:0]itf_abs;
wire [15:0]itf_shift;
assign itf_exp=(op1!=0)?exp_get+4'hf:4'h0;
assign itf_abs=(op1[15])?(~op1+1):op1;
defparam ITF_ALIGNER.result_width=16;
defparam ITF_ALIGNER.normalize_width=10;
normalize_shifter ITF_ALIGNER
(
    .datai(itf_abs),
    .left_shift_num(exp_get),
    .datao(itf_shift)
);
assign itfcalc={op1[15],itf_exp,itf_shift};
wire [15:0]fticalc;
wire [15:0]ftival;
wire ftishiftdir,belowzero;
wire [15:0]ftishift;
wire [3:0]ftiexp;
assign ftival=(sign1)?((~{6'h01,op1[9:0]})+1):{6'h01,op1[9:0]};
assign ftishiftdir=(exp1>=5'h1a)?1'b0:1'b1;
assign ftiexp=(ftishiftdir)?(4'd10-exp1[4:0]):(exp1[4:0]-4'd10);
assign belowzero=(exp1<5'h10);
bshifter16 ftishifter1
(
    .datain(ftival),
    .shiftnum(ftiexp),
    .typ({ftishiftdir,1'b1}),
    .dataout(ftishift)
);
assign fticalc=(belowzero)?16'b0:{op1[15],ftishift[14:0]};
wire [15:0]maxout,minout,ftlout;
wire [7:0] ftlexpgen,ltfexpgen;
assign maxout=(gt)?op1:op2;
assign minout=(gt)?op2:op1;
assign ftlexpgen=fullin[30:22]-8'd112;
assign ltfexpgen=exp1+8'd112;
assign ftlout={fullin[31],ftlexpgen[4:0],fullin[22:13]};
assign fullout={op1[15],ltfexpgen,val1,13'b0};
assign opout=   (!enable 		? op1		: 16'b0)|
			    ((addsel|subsel)? acalc 	: 16'b0)|
                (mulsel 		? mcalc 	: 16'b0)|
				(itfsel 		? itfcalc 	: 16'b0)|
                (ftisel 		? fticalc  	: 16'b0)|
                (maxsel 		? maxout 	: 16'b0)|
                (minsel 		? minout  	: 16'b0)|
                (ftlsel 		? ftlout  	: 16'b0); 
endmodule
